//$Id: CriterionUtils.java,v 1.2 2006/10/16 00:34:25 chaostone Exp $ /* * * Copyright c 2005-2009. * */ /******************************************************************************** * @author chaostone * * MODIFICATION DESCRIPTION * * Name Date Description * ============ ============ ============ * chaostone 2005-10-27 Created * ********************************************************************************/ package org.beanfuse.persist.hibernate; import java.sql.Date; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.beanutils.BeanMap; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.beanfuse.model.Component; import org.beanfuse.model.Entity; import org.beanfuse.model.predicates.ValidEntityKeyPredicate; import org.hibernate.Criteria; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Example; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 条件查询工具类 主要为简单实体查询提供帮助. 对样例(Example)查询提供了扩展对实体类的标识符和排除非空属性上(包括""和0)提供更进一步的支持, * 该工具类多对一关联也提供了查询支持. 对于实体类中的属性映射为Component的在原来的Example就提供支持,不用另外写给予他们属性的查询. * * @author chaostone 2005-10-28 */ public final class CriterionUtils { private CriterionUtils() { } private static final Logger logger = LoggerFactory.getLogger(CriterionUtils.class); public static void addCriterionsFor(Criteria criteria, List criterions) { for (Iterator iter = criterions.iterator(); iter.hasNext();) { criteria.add((Criterion) iter.next()); } } public static List getEntityCriterions(Object entity) { return getEntityCriterions("", entity, null, MatchMode.ANYWHERE, true); } public static List getEntityCriterions(Object entity, boolean ignoreZero) { return getEntityCriterions("", entity, null, MatchMode.ANYWHERE, ignoreZero); } public static List getEntityCriterions(Object entity, String[] excludePropertes) { return getEntityCriterions("", entity, excludePropertes, MatchMode.ANYWHERE, true); } public static List getEntityCriterions(String nestedName, Object entity) { return getEntityCriterions(nestedName, entity, null, MatchMode.ANYWHERE, true); } public static List getEntityCriterions(String nestedName, Object entity, String[] excludePropertes) { return getEntityCriterions(nestedName, entity, excludePropertes, MatchMode.ANYWHERE, true); } public static Example getExampleCriterion(Object entity) { return getExampleCriterion(entity, null, MatchMode.ANYWHERE); } public static Example getExampleCriterion(Object entity, String[] excludePropertes, MatchMode mode) { Example example = Example.create(entity) .setPropertySelector(new NotEmptyPropertySelector()); if (null != mode) { example.enableLike(mode); } if (null != excludePropertes) { for (int i = 0; i < excludePropertes.length; i++) { example.excludeProperty(excludePropertes[i]); } } return example; } /** * 获得实体类的属性和多对一属性(主键)的查询条件. (包括外键和组件以及组件内的外键),字符串类型可以采用模糊查询. * * @param entity * @param excludePropertes * @param mode * @return */ public static List getEntityCriterions(String nestedName, Object entity, String[] excludePropertes, MatchMode mode, boolean ignoreZero) { if (null == entity) { return Collections.EMPTY_LIST; } List criterions = new ArrayList(); BeanMap map = new BeanMap(entity); Set keySet = map.keySet(); Collection properties = null; if (null == excludePropertes) { List proList = new ArrayList(); proList.addAll(keySet); properties = proList; } else { properties = CollectionUtils.subtract(keySet, Arrays.asList(excludePropertes)); } properties.remove("class"); for (Iterator iter = properties.iterator(); iter.hasNext();) { String propertyName = (String) iter.next(); if (!PropertyUtils.isWriteable(entity, propertyName)) { continue; } Object value = map.get(propertyName); addCriterion(nestedName, entity, excludePropertes, propertyName, value, criterions, mode, ignoreZero); } return criterions; } public static List getEqCriterions(Object entity, String[] properties) { List criterions = new ArrayList(); BeanMap map = new BeanMap(entity); for (int i = 0; i < properties.length; i++) { criterions.add(Restrictions.eq(properties[i], map.get(properties[i]))); } return criterions; } public static List getForeignerCriterions(Object entity) { BeanMap map = new BeanMap(entity); return getForeignerCriterions(entity, map.keySet()); } public static List getForeignerCriterions(Object entity, Collection properties) { List criterions = new ArrayList(); BeanMap map = new BeanMap(entity); for (Iterator iter = properties.iterator(); iter.hasNext();) { String propertyName = (String) iter.next(); Object foreigner = map.get(propertyName); if (foreigner instanceof Entity) { BeanMap foreignerMap = new BeanMap(foreigner); Object foreignKey = foreignerMap.get(((Entity) foreigner).key()); // 该值不能为空,而且要么不是String类型,要么是不空String类型变量. if (ValidEntityKeyPredicate.getInstance().evaluate(foreignKey)) { // 在查询中添加该键值. criterions.add(Restrictions.eq(propertyName + "." + ((Entity) foreigner).key(), foreignKey)); } } } return criterions; } public static List getForeignerCriterions(Object entity, String[] properties) { return getForeignerCriterions(entity, Arrays.asList(properties)); } public static List getLikeCriterions(Object entity, String[] Properties) { return getLikeCriterions(entity, Properties, MatchMode.ANYWHERE); } /** * 返回非空字符串属性的like条件列表 * * @param entity * @param properties * @param mode * @return */ public static List getLikeCriterions(Object entity, String[] properties, MatchMode mode) { List criterions = new ArrayList(); BeanMap map = new BeanMap(entity); for (int i = 0; i < properties.length; i++) { Object value = map.get(properties[i]); if ((value instanceof String) && (StringUtils.isNotEmpty((String) value))) { criterions.add(Restrictions.like(properties[i], (String) value, mode)); } } return criterions; } /** * 返回默认采取MatchMode.ANYWHERE的实体参数map * * @param entity * @return */ public static Map getParamsMap(Entity entity) { if (null == entity) { return Collections.EMPTY_MAP; } return getParamsMap(entity, MatchMode.ANYWHERE); } /** * 将一个实体类中的非空属性及其值.<br> * 实体类中的component的属性将会级联描述,<br> * 其内部的属性完全看作没有component包装一样,但结果map中的名称是component.attr形式的. * * @param entity * ,传递null,返回空map. * @param mode * 若含有非空字符串,采用的like策略 * @return */ public static Map getParamsMap(Entity entity, MatchMode mode) { if (null == entity) { return Collections.EMPTY_MAP; } Map datas = new HashMap(); String attr = ""; try { Map beanMap = PropertyUtils.describe(entity); for (Iterator iter = beanMap.keySet().iterator(); iter.hasNext();) { attr = (String) iter.next(); Object value = PropertyUtils.getProperty(entity, attr); if (value == null) { continue; } else { addTrivialAttr(datas, attr, value, mode); if (value instanceof Entity) { String key = ((Entity) value).key(); value = PropertyUtils.getProperty(entity, attr + "." + key); if (ValidEntityKeyPredicate.getInstance().evaluate(value)) { datas.put(attr + "." + key, value); } } } } return datas; } catch (Exception e) { logger.error("[converToMap]:error occur in converToMap of bean" + entity + "with attr named " + attr, e); } return Collections.EMPTY_MAP; } static Criterion eqCriterion(String name, Object value) { logger.debug("[CriterionUtils]:name {} value {}", name, value); return Restrictions.eq(name, value); } static Criterion likeCriterion(String name, String value, MatchMode mode) { logger.debug("[CriterionUtils]:name {} value {}", name, value); return Restrictions.like(name, value, mode); } /** * 添加一个查询条件 * * @param entity * @param excludePropertes * @param path * @param value * @param criterions * @param mode */ private static void addCriterion(String nestedName, Object entity, String[] excludePropertes, String path, Object value, List criterions, MatchMode mode, boolean ignoreZero) { if (null == value) { return; } addPrimativeCriterion(nestedName + path, value, criterions, ignoreZero); if (value instanceof String) { if (StringUtils.isNotEmpty((String) value)) { criterions.add(likeCriterion(nestedName + path, (String) value, mode)); } } else if (value instanceof Entity) { BeanMap foreignerMap = new BeanMap(value); Object foreignKey = foreignerMap.get(((Entity) value).key()); // 该值不能为空,而且要么不是String类型,要么是不空String类型变量. if (ValidEntityKeyPredicate.getInstance().evaluate(foreignKey)) { // 在查询中添加该键值. criterions.add(eqCriterion(nestedName + path + "." + ((Entity) value).key(), foreignKey)); } } else if (value instanceof Component) { criterions.addAll(getComponentCriterions(nestedName, entity, path, excludePropertes, mode, ignoreZero)); } } /** * 针对内建数据类型和日期类型添加查询条件 因为很多从页面回传的""字符串在转化成数字时为0,所以这里忽略0 * * @param name * @param value * @param criterions */ private static void addPrimativeCriterion(String name, Object value, List criterions, boolean ignoreZero) { Criterion criterion = null; if (value instanceof Number) { if (ignoreZero) { if (0 != ((Number) value).intValue()) { criterion = eqCriterion(name, value); } } else { criterion = eqCriterion(name, value); } } if ((value instanceof Character) || (value instanceof Boolean)) { criterion = eqCriterion(name, value); } if ((value instanceof Date)) { criterion = eqCriterion(name, value); } if (null != criterion) { criterions.add(criterion); } } /** * 为converToMap使用的私有方法 * * @param datas * @param name * @param value * @param mode */ private static void addTrivialAttr(Map datas, String name, Object value, MatchMode mode) { if (value instanceof Number && ((Number) value).intValue() != 0) { datas.put(name, value); } if (value instanceof String && StringUtils.isNotBlank((String) value)) { StringBuilder strBuilder = new StringBuilder((String) value); if (mode.equals(MatchMode.ANYWHERE)) { strBuilder.insert(0, '%').append('%'); } else if (mode.equals(MatchMode.START)) { strBuilder.append('%'); } else if (mode.equals(MatchMode.END)) { strBuilder.insert(0, '%'); } datas.put(name, strBuilder.toString()); } if (value instanceof Component) { datas.putAll(converToMap(name, (Component) value, mode)); } if (value instanceof Entity) { try { String key = ((Entity) value).key(); Object propertyValue = PropertyUtils.getProperty(value, key); if (ValidEntityKeyPredicate.getInstance().evaluate(propertyValue)) { datas.put(name + "." + key, propertyValue); } } catch (Exception e) { logger.error("getProperty error", e); } } } private static Map converToMap(String prefix, Component component, MatchMode mode) { if (null == component) { return Collections.EMPTY_MAP; } Map datas = new HashMap(); String attr = ""; try { Map beanMap = PropertyUtils.describe(component); for (Iterator iter = beanMap.keySet().iterator(); iter.hasNext();) { attr = (String) iter.next(); Object value = PropertyUtils.getProperty(component, attr); if (value == null) { continue; } else { addTrivialAttr(datas, prefix + "." + attr, value, mode); } } return datas; } catch (Exception e) { logger.error("[converToMap]:error occur in converToMap of component" + component + "with attr named " + attr, e); } return Collections.EMPTY_MAP; } /** * 返回实体类内部组件的查询条件 * * @param entity * @param property * 组件在实体类中的名称允许级联例如outcomponent.innercomponent * @param excludePropertes * 每个元素形式如entityProperty.componentProperty * @param enableLike * @return */ private static List getComponentCriterions(String nestedName, Object entity, String property, String[] excludePropertes, MatchMode mode, boolean ignoreZero) { List criterions = new ArrayList(); Component component = null; try { component = (Component) PropertyUtils.getProperty(entity, property); } catch (Exception e) { return Collections.EMPTY_LIST; } if (null == component) { return Collections.EMPTY_LIST; } BeanMap map = new BeanMap(component); Set properties = map.keySet(); Set excludeSet = null; if (null == excludePropertes) { excludeSet = Collections.EMPTY_SET; } else { excludeSet = new HashSet(); excludeSet.addAll(Arrays.asList(excludePropertes)); } for (Iterator iter = properties.iterator(); iter.hasNext();) { String propertyName = (String) iter.next(); String cascadeName = property + "." + propertyName; if (excludeSet.contains(cascadeName) || "class".equals(propertyName)) { continue; } if (!PropertyUtils.isWriteable(component, propertyName)) { continue; } Object value = map.get(propertyName); addCriterion(nestedName, entity, excludePropertes, cascadeName, value, criterions, mode, ignoreZero); } return criterions; } public static void addSortListFor(Criteria criteria, List sortList) { if (null != sortList) { for (Iterator iter = sortList.iterator(); iter.hasNext();) { org.beanfuse.collection.Order order = (org.beanfuse.collection.Order) iter.next(); if (order.isAscending()) { criteria.addOrder(Order.asc(order.getProperty())); } else { criteria.addOrder(Order.desc(order.getProperty())); } } } } }